/*
 * Created on Mar 13, 2009
 *
 * TODO
 */
package jacky.lanlan.song.util;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * <i>若字符串符合下列格式，将被自动转换为对应的类型：</i><br>
 * <ul style="color:green">
 * <li>整数 -> Long</li>
 * <li>浮点数 -> Double</li>
 * <li>true/false -> Boolean</li>
 * <li>yyyy-MM-dd 或 yyyy/MM/dd 或 yyyy.MM.dd (比如1984-01-25) -> Date</li>
 * <li>"l_"开头，用','分割的值 (比如 {@code l_value1,value2,value3,...}) -> {@code List<Object>}
 * (递归操作，即元素也会被转换)</li>
 * <li>"m_"开头，用':'分割键值 (比如 {@code m_key1:value1,key2:value2,...} )，用','分割Entry -> {@code
 * Map<Object,Object>} (递归操作，即元素也会被转换)</li>
 * </ul>
 * 
 * @author Jacky.Song
 */
public enum StringConverter {

	Date {
		@Override
		public boolean canConvertTo(Class<?> type) {
			return type == Date.class;
		}

		@Override
		public Object convert(String string) {
			try {
				return new SimpleDateFormat("yyyy-MM-dd").parse(string.replaceAll("[\\./]", "-"));
			}
			catch (ParseException e) {
				return string;
			}
		}
	},

	// l_value1,value2,value3,...
	List {
		@Override
		public boolean canConvertTo(Class<?> type) {
			return type == List.class;
		}

		@Override
		public Object convert(String string) {
			String[] values = string.substring(2).split(",");// 去掉开头的l_再split()
			List<Object> list = new ArrayList<Object>(values.length);
			for (String v : values) {
				list.add(convertIfPossible(v));// 递归转换值
			}
			return list;
		}
	},

	// m_key1:value1,key2:value2,...
	Map {
		@Override
		public boolean canConvertTo(Class<?> type) {
			return type == Map.class;
		}

		@Override
		public Object convert(String string) {
			String[] entries = string.substring(2).split(",");// 去掉开头的m_再split()
			Map<Object, Object> map = new LinkedHashMap<Object, Object>(entries.length);
			for (String entry : entries) {
				String[] pair = entry.split(":");// 分割键和值
				map.put(convertIfPossible(pair[0]), convertIfPossible(pair[1]));// 递归转换
			}
			return map;
		}
	},

	;
	
	/**
	 * 能否转换给定字符串到目标类型
	 */
	public abstract boolean canConvertTo(Class<?> targetType);

	/**
	 * 转换给定字符串
	 */
	public abstract Object convert(String string);

	/**
	 * 注册 自定义转换器。
	 * <p>
	 * <i>如果原来已经注册过相同 pattern，则会覆盖原来的注册信息。</i>
	 * 
	 * @param pattern
	 *          匹配目标类型的字符串样式
	 * @param type
	 *          目标类型
	 * @param pe
	 *          转换器类型
	 * @see PropertyEditorManager#registerEditor(Class, Class)
	 */
	public static void registerConverter(String pattern, Class<?> type, Class<? extends PropertyEditor> pe) {
		TYPE_TABLE.put(Pattern.compile(pattern), type);
		synchronized (StringConverter.class) {
			PropertyEditorManager.registerEditor(type, pe);
		}
	}

	/**
	 * 尝试转换给定字符串，如果不能转换，返回原值。
	 * 
	 * @param string
	 *          要转换的字符串
	 * @return 如果匹配，则为对应的对象，如果不匹配，则是原string值
	 */
	public static Object convertIfPossible(String string) {
		Class<?> customType = findTargetType(string);
		if (customType != null) return convert(customType, string);
		return string;
	}
	
	// 转换字符串为目标类型
	private static Object convert(Class<?> targetType, String string) {
		// 先委托给PropertyEditor，用于转换基本类型，因为这类转换占绝大多数，所以写在前面
		PropertyEditor pe = PropertyEditorManager.findEditor(targetType);
		if (pe != null) {
			pe.setAsText(string);
			return pe.getValue();
		}
		
		// 没有找到，查默认定义的
		for (StringConverter con : values()) {
			if (con.canConvertTo(targetType)) return con.convert(string);
		}
		
		return string;
	}

	// 寻找匹配给定字符串的目标类型，如果没有，返回null
	public static Class<?> findTargetType(String string) {
		for (Pattern pattern : TYPE_TABLE.keySet()) {
			if (pattern.matcher(string).matches()) return TYPE_TABLE.get(pattern);
		}
		return null;
	}

	// 自定义转换器表
	private static final Map<Pattern, Class<?>> TYPE_TABLE = new ConcurrentHashMap<Pattern, Class<?>>(6);
	
	static {
		TYPE_TABLE.put(Pattern.compile("\\d+"), long.class);
		TYPE_TABLE.put(Pattern.compile("\\d+\\.\\d+"), double.class);
		TYPE_TABLE.put(Pattern.compile("true|false"), boolean.class);
		// Date 的 Regex，不能判断非闰年的 2月29日 情况，任何年份的 2月29日 都可以通过
		TYPE_TABLE.put(Pattern.compile("\\d{4}[-\\./](0[1-9]|1[0-2])[-\\./](0[1-9]|[12]\\d|3[01])"), Date.class);
		TYPE_TABLE.put(Pattern.compile("^(l_)(.+?,)+.+"), List.class);// l_开头，',' 分割
		TYPE_TABLE.put(Pattern.compile("^(m_)(.+?:.+?,)+.+"), Map.class);// m_开头，键值 ':' 分割，Entry ',' 分割
	}
}
